/******************************************************************************

 @File         PVRShellOS.cpp

 @Title        Qt/PVRShellOS

 @Version      

 @Copyright    Copyright (C)  Imagination Technologies Limited.

 @Platform     Non-windowed support for Symbian

 @Description  Makes programming for 3D APIs easier by wrapping window creation
               and other functions for use by a demo.

******************************************************************************/

#include "PVRShell.h"
#include "PVRShellAPI.h"
#include "PVRShellOS.h"
#include "PVRShellImpl.h"
#include <stdio.h>
#include <stdarg.h>

#include <QApplication>
#include <QMessageBox>
#include <QDesktopWidget>
#include <QKeyEvent>

/*!***************************************************************************
	Defines
*****************************************************************************/

/*!***************************************************************************
	Consts
*****************************************************************************/

/*!***************************************************************************
	Declarations
*****************************************************************************/

/*!***************************************************************************
	Class: PVRShellInit
*****************************************************************************/

/*!***********************************************************************
@Function		PVRShellOutputDebug
@Input			format			printf style format followed by arguments it requires
@Description	Writes the resultant string to the debug output (e.g. using
				printf(), OutputDebugString(), ...). Check the SDK release notes for
				details on how the string is output.
*************************************************************************/
void PVRShell::PVRShellOutputDebug(char const * const format, ...) const
{
    if(!format)
        return;

    va_list arg;
    char	buf[1024];

    va_start(arg, format);
    vsprintf(buf, format, arg);
    va_end(arg);

    // Passes the data to a platform dependant function
    m_pShellInit->OsDisplayDebugString(buf);
}

/*!***********************************************************************
 @Function		OsInit
 @description	Initialisation for OS-specific code.
*************************************************************************/
void PVRShellInit::OsInit()
{
    m_Time.restart();
}

/*!***********************************************************************
 @Function		OsInitOS
 @description	Saves instance handle and creates main window
				In this function, we save the instance handle in a global variable and
				create and display the main program window.
*************************************************************************/
bool PVRShellInit::OsInitOS()
{
    // Create our window
    m_pWindow = new PVRShellMainWindow(this);

    if(!m_pWindow)
        return false;

    m_pWindow->setWindowTitle((const char*) m_pShell->PVRShellGet(prefAppName));

    // Resize to fit the screen
    QDesktopWidget *desktop = QApplication::desktop();
    m_pWindow->resize(desktop->width(), desktop->height());
    //m_pWindow->resize(800,600);
    //TODO
    m_pWindow->showFullScreen();
    //m_pWindow->show();
    m_pShell->PVRShellSet(prefWidth, desktop->width());
    m_pShell->PVRShellSet(prefHeight, desktop->height());
    //m_pShell->PVRShellSet(prefWidth, 800);
    //m_pShell->PVRShellSet(prefHeight, 600);
    return true;
}

/*!***********************************************************************
 @Function		OsReleaseOS
 @description	Destroys main window
*************************************************************************/
void PVRShellInit::OsReleaseOS()
{
    delete m_pWindow;
    m_pWindow = 0;
}

/*!***********************************************************************
 @Function		OsExit
 @description	Destroys main window
*************************************************************************/
void PVRShellInit::OsExit()
{
    // Show the exit message to the user
    m_pShell->PVRShellOutputDebug((const char*)m_pShell->PVRShellGet(prefExitMessage));

    const char* pExitMessage = (const char*) m_pShell->PVRShellGet(prefExitMessage);

    if(pExitMessage)
    {
        QMessageBox msg;
        msg.setText("Exit message");
        msg.setInformativeText(pExitMessage);
        msg.exec();
    }
}

/*!***********************************************************************
 @Function		OsDoInitAPI
 @Return		true on success
 @description	Perform API initialisation and bring up window / fullscreen
*************************************************************************/
bool PVRShellInit::OsDoInitAPI()
{
    if(!ApiInitAPI())
        return false;

    // Give our window focus so we can capture the key events
    m_pWindow->setFocus();

    // No problem occured
    return true;
}

/*!***********************************************************************
 @Function		OsDoReleaseAPI
 @description	Clean up after we're done
*************************************************************************/
void PVRShellInit::OsDoReleaseAPI()
{
    ApiReleaseAPI();
}

/*!***********************************************************************
 @Function		OsRenderComplete
 @Returns		false when the app should quit
 @description	Main message loop / render loop
*************************************************************************/
void PVRShellInit::OsRenderComplete()
{

}

/*!***********************************************************************
 @Function		OsPixmapCopy
 @Return		true if the copy succeeded
 @description	When using pixmaps, copy the render to the display
*************************************************************************/
bool PVRShellInit::OsPixmapCopy()
{
    return false;
}

/*!***********************************************************************
 @Function		OsGetNativeDisplayType
 @Return		The 'NativeDisplayType' for EGL
 @description	Called from InitAPI() to get the NativeDisplayType
*************************************************************************/
void *PVRShellInit::OsGetNativeDisplayType()
{
    return (void*) 0;
}

/*!***********************************************************************
 @Function		OsGetNativePixmapType
 @Return		The 'NativePixmapType' for EGL
 @description	Called from InitAPI() to get the NativePixmapType
*************************************************************************/
void *PVRShellInit::OsGetNativePixmapType()
{
    // Pixmap support: return the pixmap
    return 0;
}

/*!***********************************************************************
 @Function		OsGetNativeWindowType
 @Return		The 'NativeWindowType' for EGL
 @description	Called from InitAPI() to get the NativeWindowType
*************************************************************************/
void *PVRShellInit::OsGetNativeWindowType()
{
    return m_pWindow;
}

/*!***********************************************************************
 @Function		OsGet
 @Input			prefName	Name of value to get
 @Modified		pn A pointer set to the value asked for
 @Returns		true on success
 @Description	Retrieves OS-specific data
*************************************************************************/
bool PVRShellInit::OsGet(const prefNameIntEnum prefName, int *pn)
{
    PVRSHELL_UNREFERENCED_PARAMETER(prefName);
    PVRSHELL_UNREFERENCED_PARAMETER(pn);
    return false;
}

/*!***********************************************************************
 @Function		OsGet
 @Input			prefName	Name of value to get
 @Modified		pp A pointer set to the value asked for
 @Returns		true on success
 @Description	Retrieves OS-specific data
*************************************************************************/
bool PVRShellInit::OsGet(const prefNamePtrEnum prefName, void **pp)
{
    switch(prefName)
    {
    case prefPointerLocation:
            if(m_pWindow->IsTouched())
                *pp = m_pWindow->GetTouchPosition();
            else
                return false;

            return true;

    default:
            return false;
    }
    return false;
}

/*!***********************************************************************
 @Function		OsSet
 @Input			prefName	Name of value to set
 @Input			i32Value 	The value to set our named value to
 @Returns		true on success
 @Description	Sets OS-specific data
*************************************************************************/
bool PVRShellInit::OsSet(const prefNameIntEnum prefName, const int i32Value)
{
    PVRSHELL_UNREFERENCED_PARAMETER(prefName);
    PVRSHELL_UNREFERENCED_PARAMETER(i32Value);
    return false;
}

/*!***********************************************************************
 @Function		OsDisplayDebugString
 @Input			str		string to output
 @Description	Prints a debug string
*************************************************************************/
void PVRShellInit::OsDisplayDebugString(char const * const str)
{
    fprintf(stderr, "%s", str);
}

/*!***********************************************************************
 @Function		OsGetTime
 @Return		Time in milliseconds since the beginning of the application
 @Description	Gets the time in milliseconds since the beginning of the application
*************************************************************************/
unsigned long PVRShellInit::OsGetTime()
{
    return m_Time.elapsed();
}

/*****************************************************************************
 Class: PVRShellInitOS
*****************************************************************************/

PVRShellMainWindow::PVRShellMainWindow(PVRShellInit *pInit) : m_pInit(pInit),
                                                              m_bTouch(false)
{
    setFocusPolicy(Qt::StrongFocus);
}

void PVRShellMainWindow::keyPressEvent(QKeyEvent *keyEvent)
{
    if(m_pInit)
    {
        switch(keyEvent->key())
        {
            // Exit
            case Qt::Key_Q:  m_pInit->KeyPressed(PVRShellKeyNameQUIT); return;

            // Arrow keys
            case Qt::Key_Up:    m_pInit->KeyPressed(m_pInit->m_eKeyMapUP);   return;
            case Qt::Key_Down:  m_pInit->KeyPressed(m_pInit->m_eKeyMapDOWN); return;
            case Qt::Key_Left:  m_pInit->KeyPressed(m_pInit->m_eKeyMapLEFT); return;
            case Qt::Key_Right: m_pInit->KeyPressed(m_pInit->m_eKeyMapRIGHT);return;

            // Action and select keys
            case Qt::Key_Return: m_pInit->KeyPressed(PVRShellKeyNameSELECT);  return;
            case Qt::Key_Space:  m_pInit->KeyPressed(PVRShellKeyNameACTION1); return;
            case Qt::Key_At:     m_pInit->KeyPressed(PVRShellKeyNameACTION2); return;

            // Screenshot
            case Qt::Key_S:    m_pInit->KeyPressed(PVRShellKeyNameScreenshot); return;
        }
    }

    QMainWindow::keyPressEvent(keyEvent);
}

void PVRShellMainWindow::mousePressEvent(QMouseEvent *mouseEvent)
{
    m_bTouch = mouseEvent->button() == Qt::LeftButton;

    if(m_bTouch)
    {
        QPoint pos = mouseEvent->pos();
        QSize s = size();

        m_fVec2PointerLocation[0] = pos.x() / (float) s.width();
        m_fVec2PointerLocation[1] = pos.y() / (float) s.height();
    }
}

void PVRShellMainWindow::mouseReleaseEvent(QMouseEvent *mouseEvent)
{
    m_bTouch = !(mouseEvent->button() == Qt::LeftButton);
}

void PVRShellMainWindow::mouseMoveEvent(QMouseEvent *mouseEvent)
{
    if(m_bTouch)
    {
        QPoint pos = mouseEvent->pos();
        QSize s = size();

        m_fVec2PointerLocation[0] = pos.x() / (float) s.width();
        m_fVec2PointerLocation[1] = pos.y() / (float) s.height();
    }
}

QSize PVRShellMainWindow::sizeHint()
{
    // Return the screen dimensions as our size hint
    QDesktopWidget *desktop = QApplication::desktop();
    return QSize(desktop->width(), desktop->height());
}

bool PVRShellMainWindow::IsTouched()
{
    return m_bTouch;
}

float* PVRShellMainWindow::GetTouchPosition()
{
    return &m_fVec2PointerLocation[0];
}

/*****************************************************************************
 Global code
*****************************************************************************/
PVRShellTimer::PVRShellTimer(QObject *parent) : QTimer(parent),
                                                m_pInit(0)
{
    connect(this, SIGNAL(timeout()), this, SLOT(Run()));
}

PVRShellTimer::~PVRShellTimer()
{
    DeInit();
}

void PVRShellTimer::Run()
{
    // Initialise/run/shutdown
    if(!m_pInit->Run())
    {
        stop();
        DeInit();
        QApplication::exit(0); // We wish to exit our app
    }
}

bool PVRShellTimer::Init(int argc, char **argv)
{
    m_pInit = new PVRShellInit();

    if(!m_pInit)
        return false;

    // Initialise our demo
    if(!m_pInit->Init())
    {
		fprintf(stderr, "Failed to initialise the demo\n");
        delete m_pInit;
        return false;
    }

    // Set the apps command-line
    m_pInit->CommandLine((argc-1),&argv[1]);

    m_pInit->SetAppName(argv[0]);
    m_pInit->SetReadPath(argv[0]); // TODO: Find the real read path
    m_pInit->SetWritePath(argv[0]); // TODO: Find the real write path
    return true;
}

void PVRShellTimer::DeInit()
{
    // Tidy up our demo
    delete m_pInit;
    m_pInit = 0;
}

/*!***************************************************************************
@function		main
@input			argc	count of args from OS
@input			argv	array of args from OS
@returns		result code to OS
@description	Second Main function of the program, called by main to allow for
                                efficient heap memory debugging.
*****************************************************************************/
int main(int argc, char **argv)
{
    QApplication a( argc, argv );

    PVRShellTimer timer;
    timer.Init(argc, argv);
    timer.start(0);

    // call exec()
    return a.exec();
}

/*****************************************************************************
 End of file (PVRShellOS.cpp)
*****************************************************************************/


